﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Management;

namespace System.Peppers.Wmi
{
    /// <summary>
    /// The Win32_PhysicalMemory WMI class represents a physical memory device located on a computer system and available to the operating system.
    /// </summary>
    public sealed class PhysicalMemory : SystemElement
    {
        #region Static
        private static class WmiProperties
        {
            public const string BankLabel = "BankLabel",
                Capacity = "Capacity",
                DataWidth = "DataWidth",
                DeviceLocator = "DeviceLocator",
                InterleaveDataDepth = "InterleaveDataDepth",
                InterleavePosition = "InterleavePosition",
                MemoryType = "MemoryType",
                Model = "Model",
                PartNumber = "PartNumber",
                PositionInRow = "PositionInRow",
                SerialNumber = "SerialNumber",
                SKU = "SKU",
                Speed = "Speed",
                TotalWidth = "TotalWidth",
                TypeDetail = "TypeDetail";
        }

        /// <summary>
        /// Enumerates physical memory on the system
        /// </summary>
        /// <returns>List of physical memory</returns>
        public static IEnumerable<PhysicalMemory> GetMemory()
        {
            foreach (ManagementObject item in GetInstances(WmiClasses.Win32_PhysicalMemory))
            {
                yield return new PhysicalMemory(item);
            }
        }

        /// <summary>
        /// Enumerates physical memory on a remote system
        /// </summary>
        /// <param name="remoteParams">Connection options for remote system</param>
        /// <returns>List of physical memory</returns>
        public static IEnumerable<PhysicalMemory> GetMemory(WmiRemoteParameters remoteParams)
        {
            foreach (ManagementObject item in GetInstances(WmiClasses.Win32_PhysicalMemory, remoteParams))
            {
                yield return new PhysicalMemory(item);
            }
        }
        #endregion

        internal PhysicalMemory(ManagementObject wmiObject)
            : base(wmiObject)
        { }

        /// <summary>
        /// Physically-labeled bank where the memory is located.
        /// </summary>
        public string BankLabel
        {
            get { return Convert.ToString(this[WmiProperties.BankLabel]); }
        }

        /// <summary>
        /// Total capacity of the physical memory—in bytes.
        /// </summary>
        public ulong Capacity
        {
            get { return Convert.ToUInt64(this[WmiProperties.Capacity] ?? 0); }
        }

        /// <summary>
        /// Data width of the physical memory—in bits. A data width of 0 (zero) and a total width of 8 (eight) indicates that the memory is used solely to provide error correction bits. 
        /// </summary>
        public ushort DataWidth
        {
            get { return Convert.ToUInt16(this[WmiProperties.DataWidth] ?? 0); }
        }

        /// <summary>
        /// Label of the socket or circuit board that holds the memory.  Example: "SIMM 3"
        /// </summary>
        public string DeviceLocator
        {
            get { return Convert.ToString(this[WmiProperties.DeviceLocator]); }
        }

        /// <summary>
        /// Unsigned 16-bit integer maximum number of consecutive rows of data that are accessed in a single interleaved transfer from the memory device. If the value is 0 (zero), the memory is not interleaved.
        /// </summary>
        public ushort InterleaveDataDepth
        {
            get { return Convert.ToUInt16(this[WmiProperties.InterleaveDataDepth] ?? 0); }
        }

        /// <summary>
        /// Position of the physical memory in an interleave. For example, in a 2:1 interleave, a value of "1" indicates that the memory is in the "even" position.
        /// </summary>
        public uint InterleavePosition
        {
            get { return Convert.ToUInt32(this[WmiProperties.InterleavePosition] ?? 0); }
        }

        /// <summary>
        /// Type of physical memory.
        /// </summary>
        public MemoryType MemoryType
        {
            get { return (MemoryType)Convert.ToUInt16(this[WmiProperties.MemoryType] ?? 0); }
        }

        /// <summary>
        /// Name for the physical element. 
        /// </summary>
        public string Model
        {
            get { return Convert.ToString(this[WmiProperties.Model]); }
        }

        /// <summary>
        /// Part number assigned by the organization responsible for producing or manufacturing the physical element.
        /// </summary>
        public string PartNumber
        {
            get { return Convert.ToString(this[WmiProperties.PartNumber]); }
        }

        /// <summary>
        /// Position of the physical memory in a row. For example, if it takes two 8-bit memory devices to form a 16-bit row, then a value of 2 (two) means that this memory is the second device—0 (zero) is an invalid value for this property.
        /// </summary>
        public uint PositionInRow
        {
            get { return Convert.ToUInt32(this[WmiProperties.PositionInRow] ?? 0); }
        }

        /// <summary>
        /// Manufacturer-allocated number to identify the physical element.
        /// </summary>
        public string SerialNumber
        {
            get { return Convert.ToString(this[WmiProperties.SerialNumber]); }
        }

        /// <summary>
        /// Stock keeping unit number for the physical element. 
        /// </summary>
        public string SKU
        {
            get { return Convert.ToString(this[WmiProperties.SKU]); }
        }

        /// <summary>
        /// Speed of the physical memory—in nanoseconds. 
        /// </summary>
        public uint Speed
        {
            get { return Convert.ToUInt32(this[WmiProperties.Speed] ?? 0); }
        }

        /// <summary>
        /// Total width, in bits, of the physical memory, including check or error correction bits. If there are no error correction bits, the value in this property should match what is specified for the DataWidth property. 
        /// </summary>
        public ushort TotalWidth
        {
            get { return Convert.ToUInt16(this[WmiProperties.TotalWidth] ?? 0); }
        }

        /// <summary>
        /// Type of physical memory represented.
        /// </summary>
        public MemoryTypeDetail TypeDetail
        {
            get { return (MemoryTypeDetail)Convert.ToUInt16(this[WmiProperties.TypeDetail] ?? 0); }
        }
    }
}
